home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Demos / ByCompany / TipTop_Software / TipTop / Supplement / src / tlog / tlog.m < prev   
Text File  |  1994-05-08  |  10KB  |  399 lines

  1. /* $Id$ */
  2. /*===========================================================================
  3.    This is tlog.m, derived from slog.m by Scott Hess.  His original 
  4.    header is included below.  The parent process uses the DO system to
  5.    talk to the TLogger object, rather than the Speaker/Listener mechanism.
  6.  
  7.    The program requires to be installed as suid to root.tty to run
  8.    correctly.
  9.  
  10.    Pedja <pedja@TipTop.com>
  11. ===========================================================================*/
  12.  
  13. /* Replacement for StuartLog in Stuart2.4.
  14.  *
  15.  * This program is in the public domain.  Use and abuse it as you see fit.
  16.  *
  17.  * This program requires setuid-root to run correctly.  There's
  18.  * now a Makefile that sets all of that up - so use make to compile
  19.  * this.  There will be warnings under NeXSTEP3.0.  Of course,
  20.  * consult Stuart's online documentation under Installation/slog
  21.  * for more information.
  22.  *
  23.  *
  24.  * The StuartLog tool in Stuart2.3 was a good idea, but it didn't
  25.  * quite work.  I found that it would often simply hang during the
  26.  * login process, thus either hanging Stuart or not getting the
  27.  * logging functions done.  slog takes a new approach.  It runs
  28.  * continuously from the time Stuart is launched, and the same process
  29.  * handles all logging functions.  Stuart communicates with slog
  30.  * via a private Speaker/Listener pair.  slog also monitors the
  31.  * parent Stuart process and exits on abnormal termination.
  32.  *
  33.  * Admire the code to connect to the parent process.  I think it
  34.  * cost me a kidney.
  35.  *
  36.  * scott hess
  37.  * shess@ssesco.com
  38.  */
  39. #import <objc/HashTable.h>
  40. #import <libc.h>
  41. #import <grp.h>
  42. #import <lastlog.h>
  43. #import <utmp.h>
  44. #import <ttyent.h>
  45. #import <pwd.h>
  46. #import <mach/mach.h>
  47. #import <mach/mach_error.h>
  48. #import <dpsclient/dpsclient.h>
  49. #import <mach/notify.h>
  50.  
  51. #import <remote/NXConnection.h>
  52. #import <machkit/NXPort.h>
  53.  
  54. #import "tlog.h"
  55.  
  56. @interface TLogger : Object <TLoggerProtocol>
  57. {
  58.     HashTable *slots;
  59.     int uid;
  60.     const char *name;
  61.     port_t parentNotify;
  62.     NXConnection *connection;
  63. }
  64. - run;
  65. @end
  66.  
  67. /* Locking open and close.  Though flock() is not a good general-purpose
  68.  * file locker due to NFS limitations.  It works well for this case
  69.  * since only the local machine can access the devices.
  70.  */
  71. int lopen( const char *filename, int openFlags)
  72. {
  73.     int fd=open( filename, openFlags);
  74.     flock( fd, LOCK_EX);
  75.     return fd;
  76. }
  77. int lclose( int fd)
  78. {
  79.     flock( fd, LOCK_UN);
  80.     return close( fd);
  81. }
  82. /* Fix ownerships and permissions on the named pty line. */
  83. void fixOwnership( const char *pty, int uid, int gid, int mod)
  84. {
  85.     char dev[ 64];
  86.     sprintf( dev, "/dev/%s", pty);
  87.     chown( dev, uid, gid);
  88.     chmod( dev, mod);
  89. }
  90. /* Write an entry to wtmp. */
  91. void writeWtmp( struct utmp *ut)
  92. {
  93.     int f=lopen( "/usr/adm/wtmp", O_WRONLY | O_APPEND);
  94.     if( f>=0) {
  95.     write( f, ut, sizeof( struct utmp));
  96.     lclose( f);
  97.     } else {
  98.     perror( "opening /usr/adm/wtmp");
  99.     }
  100. }
  101. /* Write an entry to utmp. */
  102. void writeUtmp( struct utmp *ut, int slot)
  103. {
  104.     if( slot>-1) {
  105.     int f=lopen( "/etc/utmp", O_WRONLY);
  106.     if( f>=0) {
  107.         lseek( f, slot*sizeof( struct utmp), L_SET);
  108.         write( f, ut, sizeof( struct utmp));
  109.         lclose( f);
  110.     } else {
  111.         perror( "opening /etc/utmp");
  112.     }
  113.     }
  114. }
  115.  
  116. @implementation TLogger
  117. /* Initialize uid to an invalid user id (0 is valid). */
  118. - init
  119. {
  120.     self=[super init];
  121.     if( self) {
  122.     uid=-1;
  123.     }
  124.     return self;
  125. }
  126. /* Find the slot in the /etc/ttys file for the given device.  Cache
  127.  * a mapping from the device name to the slot number for future use.
  128.  */
  129. -(int)getSlot:(const char *)device
  130. {
  131.     if( ![slots isKey:device]) {
  132.     struct ttyent *t;
  133.     int slot;
  134.     
  135.     setttyent();
  136.     for( slot=1; t=getttyent(); slot++) {
  137.         if( !strcmp( device, t->ty_name)) {
  138.         break;
  139.         }
  140.     }
  141.     endttyent();
  142.     if( !t) {
  143.         slot=-1;
  144.     }
  145.     if( !slots) {
  146.         slots=[HashTable allocFromZone:[self zone]];
  147.         slots=[slots initKeyDesc:"*" valueDesc:"i" capacity:0];
  148.     }
  149.     device=NXUniqueString( device);
  150.     [slots insertKey:device value:(void *)slot];
  151.     return slot;
  152.     } else {
  153.     return (int)[slots valueForKey:device];
  154.     }
  155. }
  156. /* Login a use on the given pty. */
  157. -(int)login:(const char *)pty ownerships:(int)ownership
  158.        utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog
  159. {
  160. #ifdef DEBUG
  161.   printf("tlog: login:%s ownerships:%d utmp:%d wtmp:%d lastlog:%d\n",
  162.      pty,ownership,utmp,wtmp,lastlog);
  163. #endif
  164.     /* Cache a passwd entry for the user if needed. */
  165.     if( uid==-1) {
  166.     /* Grab a passwd entry, set up uid.  This code was suggested
  167.      * by der Mouse <mouse@larry.mcrcim.mcgill.edu>
  168.      */
  169.     char *user=getenv( "USER");
  170.     struct passwd *pw=NULL;
  171.     uid=getuid();
  172.     if( user) {
  173.         pw=getpwnam( user);
  174.     }
  175.     if( !pw || (uid && (uid!=pw->pw_uid))) {
  176.         pw=getpwuid( uid);
  177.     }
  178.     if( pw) {
  179.         uid=pw->pw_uid;
  180.     }
  181.     if( pw) {
  182.         name=NXUniqueString( pw->pw_name);
  183.     } else {
  184.         name="Unknown";
  185.     }
  186.     }
  187.     if( utmp || wtmp || lastlog) {
  188.     struct utmp ut;
  189.  
  190.     /* Clean up the utmp entry. */
  191.     bzero( &ut, sizeof( ut));
  192.  
  193.     /* Set up the ut_name field if necessary. */
  194.     if( wtmp || utmp) {
  195.         strncpy( ut.ut_name, name, sizeof( ut.ut_name));
  196.     }
  197.  
  198.     /* Setup the line and time. */
  199.     strncpy( ut.ut_line, pty, sizeof( ut.ut_line));
  200.     time( &( ut.ut_time));
  201.  
  202.     /* Log to lastlog as needed. */
  203.     if( lastlog) {
  204.         int f=lopen( "/usr/adm/lastlog", O_WRONLY);
  205.         if( f>=0) {
  206.         struct lastlog llog;
  207.         bzero( &llog, sizeof( llog));
  208.         llog.ll_time=ut.ut_time;
  209.         strncpy( llog.ll_line, ut.ut_line, sizeof( llog.ll_line));
  210.         lseek( f, uid*sizeof( llog), L_SET);
  211.         write( f, &llog, sizeof( llog));
  212.         lclose( f);
  213.         } else {
  214.             perror( "opening /usr/adm/lastlog");
  215.         }
  216.     }
  217.     
  218.     /* Log to utmp and wtmp as needed. */
  219.     if( utmp) {
  220.         writeUtmp( &ut, [self getSlot:pty]);
  221.     }
  222.     if( wtmp) {
  223.         writeWtmp( &ut);
  224.     }
  225.     }
  226.     
  227.     /* If needed, set pty ownership to the new user, with permissions
  228.      * set for owner read/write, group write.  Group ownership set
  229.      * to the tty group, if available.
  230.      */
  231.     if( ownership) {
  232.     struct group *gr=getgrnam( "tty");
  233.     fixOwnership( pty, uid, gr ? gr->gr_gid : -1, 0620);
  234.     }
  235. #ifndef DO_FIXED
  236.     if(pty) free((void*)pty);
  237. #endif
  238.     return 0;
  239. }
  240. -(int)login:(const char *)pty ownerships:(int)ownership utmp:(int)utmp
  241. {
  242. #if 0
  243.     return [self login:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp];
  244. #else            /* This may make more sense. */
  245.     return [self login:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES];
  246. #endif
  247. }
  248. -(int)logout:(const char *)pty ownerships:(int)ownership
  249.     utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog
  250. {
  251. #ifdef DEBUG
  252.   printf("tlog: logout:%s ownerships:%d utmp:%d wtmp:%d lastlog:%d\n",
  253.      pty,ownership,utmp,wtmp,lastlog);
  254. #endif
  255.     if( utmp || wtmp) {
  256.     struct utmp ut;
  257.     
  258.     /* Clean up the utmp entry. */
  259.     bzero( &ut, sizeof( ut));
  260.     
  261.     strncpy( ut.ut_line, pty, sizeof( ut.ut_line));
  262.     time( &( ut.ut_time));
  263.     if( utmp) {
  264.         writeUtmp( &ut, [self getSlot:pty]);
  265.     }
  266.     if( wtmp) {
  267.         writeWtmp( &ut);
  268.     }
  269.     }
  270.  
  271.     /* If needed, set pty ownership back to root user, with permissions
  272.      * set for all read/write.  Group ownership reset to the tty
  273.      * group, if available.
  274.      */
  275.     if( ownership) {
  276.     struct group *gr=getgrnam( "tty");
  277.     fixOwnership( pty, 0, gr ? gr->gr_gid : -1, 0666);
  278.     }
  279. #ifndef DO_FIXED
  280.     if(pty) free((void*)pty);
  281. #endif
  282.     return 0;
  283. }
  284. -(int)logout:(const char *)pty ownerships:(int)ownership utmp:(int)utmp
  285. {
  286. #if 0
  287.     return [self logout:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp];
  288. #else            /* This may make more sense. */
  289.     return [self logout:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES];
  290. #endif
  291. }
  292. /* I use this routine to let Stuart _kindly_ ask slog to exit.  I
  293.  * don't want Stuart doing a kill() on slog while slog's in the middle
  294.  * of something ...
  295.  */
  296. -(oneway void)exit
  297. {
  298. #ifdef DEBUG
  299.   printf("exit...\n");
  300. #endif
  301.     exit( 0);
  302. }
  303. /* Disconnect our controlling tty and connect to the console device. */
  304. - ttyDisconnect
  305. {
  306.     int tty;
  307.  
  308.     tty=open( "/dev/tty", O_RDWR);
  309.     if( tty>-1) {
  310.     ioctl( tty, TIOCNOTTY, 0);
  311.     close( tty);
  312.     }
  313.     tty=open( "/dev/console", O_WRONLY);
  314.     setpgrp( 0, getpid());
  315.     dup2( tty, 1);
  316.     dup2( tty, 2);
  317.     if( tty!=1 && tty!=2) {
  318.     close( tty);
  319.     }
  320.     return self;
  321. }
  322.  
  323. // FIXME: This is never invoked, since [NXConnection run] does not dispatch
  324. // notify events...
  325. /* Catch inadvertant parent process death. */
  326. void notifyPortHandler( notification_t *msg, TLogger *self)
  327. {
  328. #ifdef DEBUG
  329.   printf("Parent died!\n");
  330. #endif
  331.     if( msg->notify_header.msg_id==NOTIFY_PORT_DELETED) {
  332.     if( msg->notify_port==self->parentNotify) {
  333.         [self exit];
  334.     }
  335.     }
  336. }
  337. - run
  338. {
  339.     kern_return_t ret;
  340.     msg_header_t initMsg;
  341.     extern int getppid( void);
  342.     task_t parentTask;
  343.     port_t notify;
  344.  
  345.     [self ttyDisconnect];
  346.  
  347.     connection=[NXConnection registerRoot:self];
  348.     /* Give us plenty of leeway for when people logout.
  349.      * Actually, even this isn't really that great,
  350.      * but what can you do?
  351.      */
  352.     port_set_backlog(task_self(),[[connection inPort] machPort],
  353.              PORT_BACKLOG_MAX);
  354.  
  355.     /* Find our parent's notify port. */
  356.     ret=task_by_unix_pid( task_self(), getppid(), &parentTask);
  357.     if( ret!=KERN_SUCCESS) {
  358.     printf( "tlog: Unable to get parent's task_t.\n");
  359.     exit( 1);
  360.     }
  361.     ret=task_get_notify_port( parentTask, &parentNotify);
  362.     initMsg.msg_remote_port=parentNotify;
  363.     if( ret!=KERN_SUCCESS) {
  364.     printf( "tlog: Unable to get parent's notify port.\n");
  365.     exit( 1);
  366.     }
  367.     port_allocate( task_self(), ¬ify);
  368.     task_set_notify_port( task_self(), notify);
  369.     DPSAddNotifyPortProc((DPSPortProc)notifyPortHandler,self);
  370.  
  371.     /* Set up the rest of the header. */
  372.     initMsg.msg_simple=TRUE;
  373.     initMsg.msg_size=sizeof( initMsg);
  374.     initMsg.msg_type=MSG_TYPE_NORMAL;
  375.     initMsg.msg_id=0;
  376.     
  377.     /* Including the port which our Listener listens on. */
  378.     initMsg.msg_local_port=[[connection inPort] machPort];
  379.  
  380.     /* Send it, and if successful, enter the event loop. */
  381.     ret=msg_send( &initMsg, SEND_TIMEOUT, 30000);
  382.  
  383.     if(ret==KERN_SUCCESS) [connection run];
  384.     else {
  385.       printf( "tlog: Unable to send Listener port to parent.\n");
  386.       exit(1);
  387.     }
  388.     return self;
  389. }
  390. @end
  391.  
  392. void main(void)
  393. {
  394. #ifdef DEBUG
  395.   printf("tlog:run\n");
  396. #endif
  397.   [[[TLogger alloc] init] run];
  398. }
  399.